/* PRQA S 3108++ */
/**
 * Copyright (C) 2024 Isoft Infrastructure Software Co., Ltd.
 * SPDX-License-Identifier: LGPL-2.1-only-with-exception OR  LicenseRef-Commercial-License
 *
 * This library is free software; you can redistribute it and/or modify it under the terms of the
 * GNU Lesser General Public License as published by the Free Software Foundation; version 2.1.
 * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Lesser General Public License for more details.
 * You should have received a copy of the GNU Lesser General Public License along with this library;
 * if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 * or see <https://www.gnu.org/licenses/>.
 *
 * Alternatively, this file may be used under the terms of the Isoft Infrastructure Software Co., Ltd.
 * Commercial License, in which case the provisions of the Isoft Infrastructure Software Co., Ltd.
 * Commercial License shall apply instead of those of the GNU Lesser General Public License.
 *
 * You should have received a copy of the Isoft Infrastructure Software Co., Ltd.  Commercial License
 * along with this program. If not, please find it at <https://EasyXMen.com/xy/reference/permissions.html>
 *
 ********************************************************************************
 **                                                                            **
 **  FILENAME    : sm2.c                                         	           **
 **                                                                            **
 **  Created on  :                                                             **
 **  Author      : For the world peace                                         **
 **  Vendor      :                                                             **
 **  DESCRIPTION : Implementation for SM2                					   **
 **                                                                            **
 **  SPECIFICATION(S):   AUTOSAR classic Platform 4.4.0                        **
 **                                                                            **
 *******************************************************************************/
/* PRQA S 3108-- */

/*******************************************************************************
**                      Includes                                              **
*******************************************************************************/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "sm2.h"
#include "sm2_common.h"
/*******************************************************************************
**                      Private Variable Definitions                          **
*******************************************************************************/
static uint8_t zin[18 + 32 * 6] = {
	0x00,
	0x80,
	0x31,
	0x32,
	0x33,
	0x34,
	0x35,
	0x36,
	0x37,
	0x38,
	0x31,
	0x32,
	0x33,
	0x34,
	0x35,
	0x36,
	0x37,
	0x38,
	0xFF,
	0xFF,
	0xFF,
	0xFE,
	0xFF,
	0xFF,
	0xFF,
	0xFF,
	0xFF,
	0xFF,
	0xFF,
	0xFF,
	0xFF,
	0xFF,
	0xFF,
	0xFF,
	0xFF,
	0xFF,
	0xFF,
	0xFF,
	0x00,
	0x00,
	0x00,
	0x00,
	0xFF,
	0xFF,
	0xFF,
	0xFF,
	0xFF,
	0xFF,
	0xFF,
	0xFC,
	0x28,
	0xE9,
	0xFA,
	0x9E,
	0x9D,
	0x9F,
	0x5E,
	0x34,
	0x4D,
	0x5A,
	0x9E,
	0x4B,
	0xCF,
	0x65,
	0x09,
	0xA7,
	0xF3,
	0x97,
	0x89,
	0xF5,
	0x15,
	0xAB,
	0x8F,
	0x92,
	0xDD,
	0xBC,
	0xBD,
	0x41,
	0x4D,
	0x94,
	0x0E,
	0x93,
	0x32,
	0xC4,
	0xAE,
	0x2C,
	0x1F,
	0x19,
	0x81,
	0x19,
	0x5F,
	0x99,
	0x04,
	0x46,
	0x6A,
	0x39,
	0xC9,
	0x94,
	0x8F,
	0xE3,
	0x0B,
	0xBF,
	0xF2,
	0x66,
	0x0B,
	0xE1,
	0x71,
	0x5A,
	0x45,
	0x89,
	0x33,
	0x4C,
	0x74,
	0xC7,
	0xBC,
	0x37,
	0x36,
	0xA2,
	0xF4,
	0xF6,
	0x77,
	0x9C,
	0x59,
	0xBD,
	0xCE,
	0xE3,
	0x6B,
	0x69,
	0x21,
	0x53,
	0xD0,
	0xA9,
	0x87,
	0x7C,
	0xC6,
	0x2A,
	0x47,
	0x40,
	0x02,
	0xDF,
	0x32,
	0xE5,
	0x21,
	0x39,
	0xF0,
	0xA0};

static uint8 Bn_Zero_dowhile[2] = {0};
static peace_sm2_SIGNATURE Sign;

static peace_sm3_CTX ctx;
static peace_sm2_KEY key;			 /**< Structure to hold the SM2 key pair */
static peace_sm2_SIGN_CTX verifyCtx; /**< Context structure for the signature verification */
static peace_sm2_JACOBIAN_POINT temp_P;
static peace_sm2_JACOBIAN_POINT temp_R;

static peace_sm2_BN r_verify;
static peace_sm2_BN s_verify;
static peace_sm2_BN e_verify;
static peace_sm2_BN x_verify;
static peace_sm2_BN t_verify;

static peace_sm2_BN d;
static peace_sm2_BN d_inv;
static peace_sm2_BN e;
static peace_sm2_BN k;
static peace_sm2_BN x;
static peace_sm2_BN t;
static peace_sm2_BN r;
static peace_sm2_BN s;
const peace_sm2_BN peace_sm2_TWO = {2u, 0u, 0u, 0u, 0u, 0u, 0u, 0u};
static uint8 buf[32] = {0};
static peace_sm2_SIGNATURE sig;
/*******************************************************************************
**                      Global Function Definitions                           **
*******************************************************************************/

int peace_sm2_do_sign(const peace_sm2_KEY *key, const uint8 dgst[32], peace_sm2_SIGNATURE *sig)
{

	memset(&temp_P, 0, sizeof(peace_sm2_JACOBIAN_POINT));
	peace_sm2_bn_from_bytes(d, key->private_key);

	// peace_sm2_fn_add(d_inv, d, peace_sm2_ONE);
	peace_sm2_bn_add(d_inv, d, peace_sm2_ONE);
	if (peace_sm2_bn_cmp(d_inv, peace_sm2_N) >= 0)
	{
		// If the result is greater than or equal to N, subtract N
		peace_sm2_bn_sub(d_inv, d_inv, peace_sm2_N);
	}
	if (peace_sm2_bn_is_zero(d_inv))
	{
		return E_ERROR;
	}
	// peace_sm2_fn_inv(d_inv, d_inv);
	memset(e, 0, sizeof(peace_sm2_BN));
	peace_sm2_bn_sub(e, peace_sm2_N, peace_sm2_TWO);
	peace_sm2_fn_exp(d_inv, d_inv, e);

	peace_sm2_bn_from_bytes(e, dgst);

	do
	{

		Bn_Zero_dowhile[0] = 0;
		Bn_Zero_dowhile[1] = 0;

		do
		{
			// if (peace_sm2_fn_rand(k) != 1)
			//{
			//	return E_ERROR;
			// }
			do
			{
				srand(time((void *)0));
				for (int a = 0; a < 32; a++)
				{
					buf[a] = rand() % 255;
				}
				peace_sm2_bn_from_bytes(k, buf);
			} while (peace_sm2_bn_cmp(r, peace_sm2_N) >= 0);
		} while (peace_sm2_bn_is_zero(k));

		peace_sm2_jacobian_point_mul_generator(&temp_P, k);
		peace_sm2_jacobian_point_get_xy(&temp_P, x, NULL);

		if (peace_sm2_bn_cmp(e, peace_sm2_N) >= 0)
		{
			peace_sm2_bn_sub(e, e, peace_sm2_N);
		}
		if (peace_sm2_bn_cmp(x, peace_sm2_N) >= 0)
		{
			peace_sm2_bn_sub(x, x, peace_sm2_N);
		}
		// peace_sm2_fn_add(r, e, x);
		peace_sm2_bn_add(r, e, x);
		if (peace_sm2_bn_cmp(r, peace_sm2_N) >= 0)
		{
			// If the result is greater than or equal to N, subtract N
			peace_sm2_bn_sub(r, r, peace_sm2_N);
		}

		peace_sm2_bn_add(t, r, k);
		if (peace_sm2_bn_is_zero(r) || peace_sm2_bn_cmp(t, peace_sm2_N) == 0)
		{

			Bn_Zero_dowhile[0] = 1;
		}

		if (Bn_Zero_dowhile[0] != 1)
		{

			peace_sm2_fn_mul(t, r, d);
			peace_sm2_fn_sub(k, k, t);
			peace_sm2_fn_mul(s, d_inv, k);
		}

		if (peace_sm2_bn_is_zero(s))
		{
			Bn_Zero_dowhile[1] = 1;
		}
	} while ((Bn_Zero_dowhile[0] != 0) || (Bn_Zero_dowhile[1] != 0));

	peace_sm2_bn_to_bytes(r, sig->r);
	peace_sm2_bn_to_bytes(s, sig->s);
	memset(&temp_P, 0, sizeof(peace_sm2_JACOBIAN_POINT));
	return 1;
}

int peace_sm2_do_verify(const peace_sm2_KEY *key, const uint8 dgst[32], const peace_sm2_SIGNATURE *sig)
{

	memset(&temp_P, 0, sizeof(peace_sm2_JACOBIAN_POINT));
	memset(&temp_R, 0, sizeof(peace_sm2_JACOBIAN_POINT));
	peace_sm2_jacobian_point_from_bytes(&temp_P, (const uint8 *)&key->public_key);

	peace_sm2_bn_from_bytes(r_verify, sig->r);
	peace_sm2_bn_from_bytes(s_verify, sig->s);

	if (peace_sm2_bn_is_zero(r_verify) == 1 || peace_sm2_bn_cmp(r_verify, peace_sm2_N) >= 0 || peace_sm2_bn_is_zero(s_verify) == 1 || peace_sm2_bn_cmp(s_verify, peace_sm2_N) >= 0)
	{

		return E_ERROR;
	}

	peace_sm2_bn_from_bytes(e_verify, dgst);

	// peace_sm2_fn_add(t_verify, r_verify, s_verify);
	peace_sm2_bn_add(t_verify, r_verify, s_verify);
	if (peace_sm2_bn_cmp(t_verify, peace_sm2_N) >= 0)
	{
		// If the result is greater than or equal to N, subtract N
		peace_sm2_bn_sub(t_verify, t_verify, peace_sm2_N);
	}
	if (peace_sm2_bn_is_zero(t_verify))
	{

		return E_ERROR;
	}

	peace_sm2_jacobian_point_mul_sum(&temp_R, t_verify, &temp_P, s_verify);
	peace_sm2_jacobian_point_get_xy(&temp_R, x_verify, NULL);

	if (peace_sm2_bn_cmp(e_verify, peace_sm2_N) >= 0)
	{
		peace_sm2_bn_sub(e_verify, e_verify, peace_sm2_N);
	}
	if (peace_sm2_bn_cmp(x_verify, peace_sm2_N) >= 0)
	{
		peace_sm2_bn_sub(x_verify, x_verify, peace_sm2_N);
	}
	// peace_sm2_fn_add(e_verify, e_verify, x_verify);
	peace_sm2_bn_add(e_verify, e_verify, x_verify);
	if (peace_sm2_bn_cmp(e_verify, peace_sm2_N) >= 0)
	{
		// If the result is greater than or equal to N, subtract N
		peace_sm2_bn_sub(e_verify, e_verify, peace_sm2_N);
	}

	if (peace_sm2_bn_cmp(e_verify, r_verify) != 0)
	{

		return E_ERROR;
	}
	return 1;
}

int peace_sm2_signature_to_der(const peace_sm2_SIGNATURE *sig, uint8 **out, uint32 *outlen)
{
	uint32 len = 0;
	if (!sig)
	{
		return 0;
	}
	if (peace_asn1_integer_to_der(sig->r, 32, NULL, &len) != 1 || peace_asn1_integer_to_der(sig->s, 32, NULL, &len) != 1 || peace_asn1_sequence_header_to_der(len, out, outlen) != 1 || peace_asn1_integer_to_der(sig->r, 32, out, outlen) != 1 || peace_asn1_integer_to_der(sig->s, 32, out, outlen) != 1)
	{

		return E_ERROR;
	}
	return 1;
}

int peace_sm2_signature_from_der(peace_sm2_SIGNATURE *sig, const uint8 **in, uint32 *inlen)
{
	int ret;
	const uint8 *d;
	uint32 dlen;
	const uint8 *r;
	uint32 rlen;
	const uint8 *s;
	uint32 slen;

	if ((ret = peace_asn1_type_from_der(peace_asn1_TAG_SEQUENCE, &d, &dlen, in, inlen)) != 1)
	{
		if (ret < 0)

			return ret;
	}
	if (peace_asn1_integer_from_der(&r, &rlen, &d, &dlen) != 1 || peace_asn1_integer_from_der(&s, &slen, &d, &dlen) != 1 || peace_asn1_length_le(rlen, 32) != 1 || peace_asn1_length_le(slen, 32) != 1 || peace_asn1_length_is_zero(dlen) != 1)
	{

		return E_ERROR;
	}
	memset(sig, 0, sizeof(*sig));
	memcpy(sig->r + 32 - rlen, r, rlen);
	memcpy(sig->s + 32 - slen, s, slen);
	return 1;
}

int peace_sm2_sign_internal(const peace_sm2_KEY *key, const uint8 dgst[32], uint8 *sigbuf, uint8 *sign_R, uint8 *sign_S, uint32 *siglen)
{
	uint8 i;
	if (!key || !dgst || !sigbuf || !siglen)
	{

		return E_ERROR;
	}

	if (peace_sm2_do_sign(key, dgst, &Sign) != 1)
	{

		return E_ERROR;
	}

	*siglen = 0;
	for (i = 0; i < 32; i++)
	{
		sign_R[i] = Sign.r[i];
		sign_S[i] = Sign.s[i];
	}
	if (peace_sm2_signature_to_der(&Sign, &sigbuf, siglen) != 1)
	{

		return E_ERROR;
	}
	return 1;
}

int peace_sm2_verify_internal(const peace_sm2_KEY *key, const uint8 dgst[32], const uint8 *sigbuf, uint32 siglen)
{

	if (!key || !dgst || !sigbuf || !siglen)
	{

		return E_ERROR;
	}

	if (peace_sm2_signature_from_der(&sig, &sigbuf, &siglen) != 1 || peace_asn1_length_is_zero(siglen) != 1)
	{

		return E_ERROR;
	}
	if (peace_sm2_do_verify(key, dgst, &sig) != 1)
	{

		return E_ERROR;
	}
	return 1;
}

int sm2ComputeZ(uint8 z[32], const peace_sm2_POINT *pub, const char *id, uint32 idlen)
{

	if (!z || !pub || !id)
	{

		return E_ERROR;
	}

	memcpy(&zin[18 + 32 * 4], pub->x, 32);
	memcpy(&zin[18 + 32 * 5], pub->y, 32);

	peace_sm3_Init(&ctx);
	if (strcmp(id, PEACE_SM2_DEFAULT_ID) == 0)
	{
		peace_sm3_update(&ctx, zin, sizeof(zin));
	}
	else
	{
		uint8 idbits[2];
		idbits[0] = (uint8)(idlen >> 5);
		idbits[1] = (uint8)(idlen << 3);
		peace_sm3_update(&ctx, idbits, 2);
		peace_sm3_update(&ctx, (uint8 *)id, idlen);
		peace_sm3_update(&ctx, zin + 18, 32 * 6);
	}
	peace_sm3_finish(&ctx, z);
	return 1;
}

Std_ReturnType peace_sm2_sign_init(peace_sm2_SIGN_CTX *ctx, const peace_sm2_KEY *key, const char *id, uint32 idlen)
{
	if (!ctx || !key)
	{

		return E_NOT_OK;
	}
	ctx->key = *key;
	peace_sm3_Init(&ctx->peace_sm3_ctx);

	if (id)
	{
		uint8 z[peace_sm3_DIGEST_SIZE];
		if (idlen <= 0 || idlen > PEACE_SM2_MAX_ID_LENGTH)
		{

			return E_NOT_OK;
		}
		sm2ComputeZ(z, &key->public_key, id, idlen);
		peace_sm3_update(&ctx->peace_sm3_ctx, z, sizeof(z));
	}
	return E_OK;
}

Std_ReturnType peace_sm2_sign_update(peace_sm2_SIGN_CTX *ctx, const uint8 *data, uint32 datalen)
{
	if (!ctx)
	{

		return E_NOT_OK;
	}
	if (data && datalen > 0)
	{
		peace_sm3_update(&ctx->peace_sm3_ctx, data, datalen);
	}
	return E_OK;
}

Std_ReturnType peace_sm2_sign_finish(peace_sm2_SIGN_CTX *ctx, uint8 *sig, uint8 *Sig_R, uint8 *Sig_S, uint32 *siglen)
{
	uint8 sign_dgst[peace_sm3_DIGEST_SIZE];
	if (!ctx || !sig || !siglen)
	{
		return E_NOT_OK;
	}
	// memset(sign_dgst, 0, peace_sm3_DIGEST_SIZE);
	peace_sm3_finish(&ctx->peace_sm3_ctx, sign_dgst);
	// printf("digest\n");
	// for (int i = 0; i < peace_sm3_DIGEST_SIZE; i++)
	//{
	//	printf("%02x", dgst[i]);
	// }
	// printf("\n");

	if (peace_sm2_sign_internal(&ctx->key, sign_dgst, sig, Sig_R, Sig_S, siglen) != 1)
	{

		return E_NOT_OK;
	}
	return E_OK;
}

int peace_sm2_verify_init(peace_sm2_SIGN_CTX *ctx, const peace_sm2_KEY *key, const char *id, uint32 idlen)
{
	if (!ctx || !key)
	{

		return E_ERROR;
	}
	memset(ctx, 0, sizeof(*ctx));
	ctx->key.public_key = key->public_key;
	peace_sm3_Init(&ctx->peace_sm3_ctx);

	if (id)
	{
		uint8 z[peace_sm3_DIGEST_SIZE];
		if (idlen <= 0 || idlen > PEACE_SM2_MAX_ID_LENGTH)
		{

			return E_ERROR;
		}
		sm2ComputeZ(z, &key->public_key, id, idlen);
		peace_sm3_update(&ctx->peace_sm3_ctx, z, sizeof(z));
	}
	return 1;
}

int peace_sm2_verify_update(peace_sm2_SIGN_CTX *ctx, const uint8 *data, uint32 datalen)
{
	if (!ctx)
	{
		return E_ERROR;
	}
	if (data && datalen > 0)
	{
		peace_sm3_update(&ctx->peace_sm3_ctx, data, datalen);
	}
	return 1;
}

int peace_sm2_verify_finish(peace_sm2_SIGN_CTX *ctx, const uint8 *sig, uint32 siglen)
{
	// uint8 dgst[peace_sm3_DIGEST_SIZE];
	uint8 verify_dgst[peace_sm3_DIGEST_SIZE];
	if (!ctx || !sig)
	{

		return E_ERROR;
	}
	// memset(verify_dgst, 0, peace_sm3_DIGEST_SIZE);
	peace_sm3_finish(&ctx->peace_sm3_ctx, verify_dgst);
	if (peace_sm2_verify_internal(&ctx->key, verify_dgst, sig, siglen) != 1)
	{

		return E_ERROR;
	}
	return 1;
}
static int peace_sm2_kdf(const uint8_t *in, size_t inlen, size_t outlen, uint8_t *out)
{
	peace_sm3_CTX ctx;
	uint8_t counter_be[4];
	uint8_t dgst[peace_sm3_DIGEST_SIZE];
	uint32_t counter = 1;
	size_t len;

	while (outlen)
	{
		PUTU32(counter_be, counter);
		counter++;

		peace_sm3_Init(&ctx);
		peace_sm3_update(&ctx, in, inlen);
		peace_sm3_update(&ctx, counter_be, sizeof(counter_be));
		peace_sm3_finish(&ctx, dgst);

		len = outlen < peace_sm3_DIGEST_SIZE ? outlen : peace_sm3_DIGEST_SIZE;
		memcpy(out, dgst, len);
		out += len;
		outlen -= len;
	}

	memset(&ctx, 0, sizeof(peace_sm3_CTX));
	memset(dgst, 0, sizeof(dgst));
	return 1;
}

static int all_zero(const uint8_t *buf, size_t len)
{
	size_t i;
	for (i = 0; i < len; i++)
	{
		if (buf[i])
		{
			return 0;
		}
	}
	return 1;
}

 /**
   * @brief Encrypts plaintext using the SM2 public key encryption algorithm.
   *
   * This function performs encryption of the input plaintext using the specified SM2 public key.
   * It generates a random number k, computes points C1 and kP, applies a key derivation function (KDF),
   * and finally calculates the hash using the SM3 algorithm. If the generated ciphertext is all zeros,
   * it retries the encryption process.
   *
   * @param[in] key Pointer to the SM2 public key structure.
   * @param[in] in Pointer to the input plaintext buffer.
   * @param[in] inlen Length of the input plaintext in bytes.
   * @param[out] out Pointer to the output ciphertext structure where the result will be stored.
   *
   * @return Returns 1 on successful encryption, E_ERROR if an error occurs.
   *
   * @pre The input plaintext length must be within the defined range [peace_sm2_MIN_PLAINTEXT_SIZE, peace_sm2_MAX_PLAINTEXT_SIZE].
   * @post Sensitive data used during encryption is securely cleared from memory.
   *
   * @note This function ensures that the encryption process is secure and compliant with SM2 standards.
   */
int	peace_sm2_do_encrypt(const peace_sm2_KEY *key, const uint8_t *in, size_t inlen, peace_sm2_CIPHERTEXT *out)
{
	peace_sm2_BN k;
	peace_sm2_JACOBIAN_POINT _P, *P = &_P;
	peace_sm2_JACOBIAN_POINT _C1, *C1 = &_C1;
	peace_sm2_JACOBIAN_POINT _kP, *kP = &_kP;
	uint8_t x2y2[64];
	peace_sm3_CTX peace_sm3_ctx;

	// Validate input plaintext length
	if (!(peace_sm2_MIN_PLAINTEXT_SIZE <= inlen && inlen <= peace_sm2_MAX_PLAINTEXT_SIZE))
	{

		return E_ERROR;
	}

	// Convert public key bytes to Jacobian point P
	peace_sm2_jacobian_point_from_bytes(P, (uint8_t *)&key->public_key);

retry:

	do
	{
		// Generate a non-zero random number k
		if (peace_sm2_fn_rand(k) != 1)
		{

			return E_ERROR;
		}
	} while (peace_sm2_bn_is_zero(k));

	// Compute C1 = k * generator		
	peace_sm2_jacobian_point_mul_generator(C1, k);
	peace_sm2_jacobian_point_to_bytes(C1, (uint8_t *)&out->point);

	// Compute kP = k * P
	peace_sm2_jacobian_point_mul(kP, k, P);
	peace_sm2_jacobian_point_to_bytes(kP, x2y2);

	// Apply key derivation function (KDF)
	peace_sm2_kdf(x2y2, 64, inlen, out->ciphertext);

	// Check if the generated ciphertext is all zeros
	if (all_zero(out->ciphertext, inlen))
	{
		goto retry;
	}

	// XOR the plaintext with the derived key to produce the ciphertext
	peace_sm2_memxor(out->ciphertext, out->ciphertext, in, inlen);
	out->ciphertext_size = (uint32_t)inlen;

	// Initialize SM3 context and update with necessary data
	peace_sm3_Init(&peace_sm3_ctx);
	peace_sm3_update(&peace_sm3_ctx, x2y2, 32);
	peace_sm3_update(&peace_sm3_ctx, in, inlen);
	peace_sm3_update(&peace_sm3_ctx, x2y2 + 32, 32);
	peace_sm3_finish(&peace_sm3_ctx, out->hash);

	// Securely clear sensitive data from memory
	memset(k, 0, sizeof(k));
	memset(kP, 0, sizeof(peace_sm2_JACOBIAN_POINT));
	memset(x2y2, 0, sizeof(x2y2));
	return 1;
}

/**
 * @brief Encrypts a fixed-length plaintext using the SM2 algorithm.
 *
 * This function performs encryption of a fixed-length plaintext using the SM2 public key cryptography algorithm. 
 * It involves elliptic curve operations, hash functions, and random number generation to produce the ciphertext.
 * The function ensures that the generated ciphertext adheres to the specified point size format.
 *
 * @param[in] key Pointer to the SM2 public key structure used for encryption.
 * @param[in] in Pointer to the plaintext data to be encrypted.
 * @param[in] inlen Length of the plaintext data.
 * @param[in] point_size Size of the output ciphertext point (compact, typical, or max).
 * @param[out] out Pointer to the structure where the resulting ciphertext will be stored.
 *
 * @return Returns 1 if encryption is successful; otherwise, returns E_ERROR.
 *
 * @note The function retries up to 200 times if the generated random point does not meet the required format.
 */
int peace_sm2_do_encrypt_fixlen(const peace_sm2_KEY *key, const uint8_t *in, size_t inlen, int point_size, peace_sm2_CIPHERTEXT *out)
{
	unsigned int trys = 200;
	peace_sm2_BN k;
	peace_sm2_JACOBIAN_POINT _P, *P = &_P;
	peace_sm2_JACOBIAN_POINT _C1, *C1 = &_C1;
	peace_sm2_JACOBIAN_POINT _kP, *kP = &_kP;
	uint8_t x2y2[64];
	peace_sm3_CTX peace_sm3_ctx;

	if (!(peace_sm2_MIN_PLAINTEXT_SIZE <= inlen && inlen <= peace_sm2_MAX_PLAINTEXT_SIZE))
	{

		return E_ERROR;
	}

	switch (point_size)
	{
	case peace_sm2_ciphertext_compact_point_size:
	case peace_sm2_ciphertext_typical_point_size:
	case peace_sm2_ciphertext_max_point_size:
		break;
	default:

		return E_ERROR;
	}

	peace_sm2_jacobian_point_from_bytes(P, (uint8_t *)&key->public_key);

retry:

	do
	{
		if (peace_sm2_fn_rand(k) != 1)
		{

			return E_ERROR;
		}
	} while (peace_sm2_bn_is_zero(k));

	peace_sm2_jacobian_point_mul_generator(C1, k);
	peace_sm2_jacobian_point_to_bytes(C1, (uint8_t *)&out->point);

	if (trys)
	{
		size_t len = 0;
		peace_asn1_integer_to_der(out->point.x, 32, NULL, &len);
		peace_asn1_integer_to_der(out->point.y, 32, NULL, &len);
		if (len != point_size)
		{
			trys--;
			goto retry;
		}
	}
	else
	{
		memset(k, 0, sizeof(k));

		return E_ERROR;
	}

	peace_sm2_jacobian_point_mul(kP, k, P);
	peace_sm2_jacobian_point_to_bytes(kP, x2y2);

	peace_sm2_kdf(x2y2, 64, inlen, out->ciphertext);

	if (all_zero(out->ciphertext, inlen))
	{
		goto retry;
	}

	peace_sm2_memxor(out->ciphertext, out->ciphertext, in, inlen);
	out->ciphertext_size = (uint32_t)inlen;

	peace_sm3_Init(&peace_sm3_ctx);
	peace_sm3_update(&peace_sm3_ctx, x2y2, 32);
	peace_sm3_update(&peace_sm3_ctx, in, inlen);
	peace_sm3_update(&peace_sm3_ctx, x2y2 + 32, 32);
	peace_sm3_finish(&peace_sm3_ctx, out->hash);

	memset(k, 0, sizeof(k));
	memset(kP, 0, sizeof(peace_sm2_JACOBIAN_POINT));
	memset(x2y2, 0, sizeof(x2y2));
	return 1;
}

/**
 * @brief Decrypts ciphertext using the SM2 private key decryption algorithm.
 *
 * This function performs decryption of the input ciphertext using the specified SM2 private key. 
 * It verifies that the point C2 is on the curve, computes point C1 using the private key, applies a key derivation function (KDF), 
 * and finally checks the hash to ensure integrity. If any step fails, it returns an error.
 *
 * @param[in] key Pointer to the SM2 private key structure.
 * @param[in] in Pointer to the input ciphertext structure.
 * @param[out] out Pointer to the output plaintext buffer where the result will be stored.
 * @param[out] outlen Pointer to the size of the output plaintext buffer.
 *
 * @return Returns 1 on successful decryption, E_ERROR if an error occurs.
 *
 * @pre The input ciphertext must be valid and the point C2 must be on the elliptic curve.
 * @post Sensitive data used during decryption is securely cleared from memory.
 *
 * @note This function ensures that the decryption process is secure and compliant with SM2 standards.
 */
int peace_sm2_do_decrypt(const peace_sm2_KEY *key, const peace_sm2_CIPHERTEXT *in, uint8_t *out, size_t *outlen)
{
	int ret = -1;
	peace_sm2_BN d;
	peace_sm2_JACOBIAN_POINT _C1, *C1 = &_C1;
	peace_sm2_JACOBIAN_POINT _C2, *C2 = &_C2;
	uint8_t x2y2[64];
	peace_sm3_CTX peace_sm3_ctx;
	uint8_t hash[32];

	// Convert point C2 from bytes to Jacobian point and verify it is on the curve
	peace_sm2_jacobian_point_from_bytes(C2, (uint8_t *)&in->point);
	if (!peace_sm2_jacobian_point_is_on_curve(C2))
	{

		return E_ERROR;
	}

    // Convert private key bytes to big number d
	peace_sm2_bn_from_bytes(d, key->private_key);
	// Compute C1 = d * C2
	peace_sm2_jacobian_point_mul(C1, d, C2);
	
	 // Convert C1 to bytes and apply key derivation function (KDF)
	peace_sm2_jacobian_point_to_bytes(C1, x2y2);
	peace_sm2_kdf(x2y2, 64, in->ciphertext_size, out);
	// Check if the derived plaintext is all zeros
	if (all_zero(out, in->ciphertext_size))
	{

		goto end;
	}
	// XOR the derived key with the ciphertext to produce the plaintext
	peace_sm2_memxor(out, out, in->ciphertext, in->ciphertext_size);
	*outlen = in->ciphertext_size;

	// Initialize SM3 context and update with necessary data
	peace_sm3_Init(&peace_sm3_ctx);
	peace_sm3_update(&peace_sm3_ctx, x2y2, 32);
	peace_sm3_update(&peace_sm3_ctx, out, in->ciphertext_size);
	peace_sm3_update(&peace_sm3_ctx, x2y2 + 32, 32);
	peace_sm3_finish(&peace_sm3_ctx, hash);

	// Verify the hash to ensure integrity
	if (memcmp(in->hash, hash, sizeof(hash)) != 0)
	{

		goto end;
	}
	ret = 1;

end:
	// Securely clear sensitive data from memory
	memset(d, 0, sizeof(d));
	memset(C1, 0, sizeof(peace_sm2_JACOBIAN_POINT));
	memset(x2y2, 0, sizeof(x2y2));
	return ret;
}

int peace_sm2_ciphertext_to_der(const peace_sm2_CIPHERTEXT *C, uint8_t **out, size_t *outlen)
{
	size_t len = 0;
	if (!C)
	{
		return 0;
	}
	if (peace_asn1_integer_to_der(C->point.x, 32, NULL, &len) != 1 || peace_asn1_integer_to_der(C->point.y, 32, NULL, &len) != 1 || peace_asn1_octet_string_to_der(C->hash, 32, NULL, &len) != 1 || peace_asn1_octet_string_to_der(C->ciphertext, C->ciphertext_size, NULL, &len) != 1 || peace_asn1_sequence_header_to_der(len, out, outlen) != 1 || peace_asn1_integer_to_der(C->point.x, 32, out, outlen) != 1 || peace_asn1_integer_to_der(C->point.y, 32, out, outlen) != 1 || peace_asn1_octet_string_to_der(C->hash, 32, out, outlen) != 1 || peace_asn1_octet_string_to_der(C->ciphertext, C->ciphertext_size, out, outlen) != 1)
	{

		return E_ERROR;
	}
	return 1;
}

/**
 * @brief Parses an SM2 ciphertext from DER encoding and populates the provided ciphertext structure.
 *
 * This function decodes a DER-encoded SM2 ciphertext into its components and stores them in the provided 
 * `peace_sm2_CIPHERTEXT` structure. It validates the ASN.1 structure and ensures that all fields are within 
 * expected lengths and constraints.
 *
 * @param C Pointer to the `peace_sm2_CIPHERTEXT` structure where the parsed data will be stored.
 * @param in Pointer to a pointer to the input buffer containing the DER-encoded ciphertext. 
 *           After parsing, this pointer is updated to point past the parsed data.
 * @param inlen Pointer to a size_t variable indicating the length of the input buffer. 
 *              After parsing, this value is updated to reflect the remaining bytes in the buffer.
 *
 * @return Returns 1 on success, or an error code if any step fails.
 */
int peace_sm2_ciphertext_from_der(peace_sm2_CIPHERTEXT *C, const uint8_t **in, size_t *inlen)
{
	int ret;
	const uint8_t *d;
	size_t dlen;
	const uint8_t *x;
	const uint8_t *y;
	const uint8_t *hash;
	const uint8_t *c;
	size_t xlen, ylen, hashlen, clen;

	if ((ret = peace_asn1_type_from_der(peace_asn1_TAG_SEQUENCE, &d, &dlen, in, inlen)) != 1)
	{
		if (ret < 0)

			return ret;
	}
	if (peace_asn1_integer_from_der(&x, &xlen, &d, &dlen) != 1 || peace_asn1_length_le(xlen, 32) != 1)
	{

		return E_ERROR;
	}
	if (peace_asn1_integer_from_der(&y, &ylen, &d, &dlen) != 1 || peace_asn1_length_le(ylen, 32) != 1)
	{

		return E_ERROR;
	}
	if (peace_asn1_octet_string_from_der(&hash, &hashlen, &d, &dlen) != 1 || peace_asn1_check(hashlen == 32) != 1)
	{

		return E_ERROR;
	}
	if (peace_asn1_octet_string_from_der(&c, &clen, &d, &dlen) != 1

		|| peace_asn1_length_le(clen, peace_sm2_MAX_PLAINTEXT_SIZE) != 1)
	{

		return E_ERROR;
	}
	if (peace_asn1_length_is_zero(dlen) != 1)
	{

		return E_ERROR;
	}
	memset(C, 0, sizeof(peace_sm2_CIPHERTEXT));
	memcpy(C->point.x + 32 - xlen, x, xlen);
	memcpy(C->point.y + 32 - ylen, y, ylen);
	if (peace_sm2_point_is_on_curve(&C->point) != 1)
	{

		return E_ERROR;
	}
	memcpy(C->hash, hash, hashlen);
	memcpy(C->ciphertext, c, clen);
	C->ciphertext_size = (uint8_t)clen;
	return 1;
}

int peace_sm2_encrypt(const peace_sm2_KEY *key, const uint8_t *in, size_t inlen, uint8_t *out, size_t *outlen)
{
	peace_sm2_CIPHERTEXT C;

	if (!key || !in || !out || !outlen)
	{

		return E_ERROR;
	}
	if (!inlen)
	{

		return E_ERROR;
	}

	if (peace_sm2_do_encrypt(key, in, inlen, &C) != 1)
	{

		return E_ERROR;
	}
	*outlen = 0;
	if (peace_sm2_ciphertext_to_der(&C, &out, outlen) != 1)
	{

		return E_ERROR;
	}
	return E_OK;
}

int peace_sm2_encrypt_fixlen(const peace_sm2_KEY *key, const uint8_t *in, size_t inlen, int point_size, uint8_t *out, size_t *outlen)
{
	peace_sm2_CIPHERTEXT C;

	if (!key || !in || !out || !outlen)
	{

		return E_ERROR;
	}
	if (!inlen)
	{

		return E_ERROR;
	}

	if (peace_sm2_do_encrypt_fixlen(key, in, inlen, point_size, &C) != 1)
	{

		return E_ERROR;
	}
	*outlen = 0;
	if (peace_sm2_ciphertext_to_der(&C, &out, outlen) != 1)
	{

		return E_ERROR;
	}
	return 1;
}

int peace_sm2_decrypt(const peace_sm2_KEY *key, const uint8_t *in, size_t inlen, uint8_t *out, size_t *outlen)
{
	peace_sm2_CIPHERTEXT C;

	if (!key || !in || !out || !outlen)
	{

		return E_ERROR;
	}
	if (peace_sm2_ciphertext_from_der(&C, &in, &inlen) != 1 || peace_asn1_length_is_zero(inlen) != 1)
	{

		return E_ERROR;
	}
	if (peace_sm2_do_decrypt(key, &C, out, outlen) != 1)
	{

		return E_ERROR;
	}
	return E_OK;
}
Std_ReturnType peace_sm2_sign(const uint8 *buf, uint32 buf_length, uint8 *Private_Key, uint8 *Publickey, uint8 *Sig_Result, uint8 *Sig_R, uint8 *Sig_S, uint32 *siglen)
{
	Std_ReturnType ret = E_OK;
	char *id = PEACE_SM2_DEFAULT_ID;
	peace_sm2_SIGN_CTX sign_ctx;
	uint32 len;

	uint8 i = 0;
	len = buf_length;

	for (i = 0; i < 32; i++)
	{
		key.private_key[i] = Private_Key[i];
		key.public_key.x[i] = Publickey[i];
		key.public_key.y[i] = Publickey[i + 32];
	}

	ret = peace_sm2_sign_init(&sign_ctx, &key, id, strlen(id));
	if (ret == E_OK)
	{
		ret = peace_sm2_sign_update(&sign_ctx, buf, len);
	}

	if (ret == E_OK)
	{
		ret = peace_sm2_sign_finish(&sign_ctx, Sig_Result, Sig_R, Sig_S, siglen);
	}
	return ret;
}

/**
 * @brief Verifies the signature using SM2 algorithm.
 *
 * This function initializes the SM2 verification context,
 * processes the input data, and checks the signature validity.
 *
 * @param[in] buf Pointer to the input buffer containing the message.
 * @param[in] bufLength Length of the input buffer.
 * @param[in] privateKey Pointer to the private key used for the verification.
 * @param[in] publicKey Pointer to the public key associated with the private key.
 * @param[out] sigVerifyResult Pointer to the buffer where the verification result will be stored.
 *
 * @return Std_ReturnType E_OK if the verification is successful, otherwise E_NOT_OK.
 */

Std_ReturnType peace_sm2_verify(const uint8 *buf, uint32 bufLength, uint8 *privateKey, uint8 *publicKey, const uint8 *sigResult, uint32 sigLen)
{
	Std_ReturnType verifyRet = E_OK;
	const char *id = PEACE_SM2_DEFAULT_ID; /**< Default identifier for the verification */

	int verifyReturn; /**< Verification result */

	for (int i = 0; i < 32; i++)
	{
		key.private_key[i] = privateKey[i];
		key.public_key.x[i] = publicKey[i];
		key.public_key.y[i] = publicKey[i + 32];
	}

	if (peace_sm2_verify_init(&verifyCtx, &key, id, strlen(id)) != 1)
	{
		verifyRet = E_NOT_OK; /**< Set return value to E_NOT_OK on initialization failure */
	}

	if (peace_sm2_verify_update(&verifyCtx, buf, bufLength) != 1)
	{
		verifyRet = E_NOT_OK; /**< Set return value to E_NOT_OK on update failure */
	}

	if ((verifyReturn = peace_sm2_verify_finish(&verifyCtx, sigResult, sigLen)) < 0)
	{
		verifyRet = E_NOT_OK; /**< Set return value to E_NOT_OK on finish failure */
	}

	if (verifyReturn == 1)
	{
		verifyRet = E_OK; /**< Set return value to E_NOT_OK if the verification failed */
	}

	return verifyRet; /**< Return the result of the verification */
}
